plantuml-proxyのprototype code
このあとgitに移した
2020-12-27 少しcodeを書いてみる
directory layout
code:text
plantuml-maker
├─api/
├─.gitignore
├─.vercelignore
├─LICENSE
├─README.md
└─vercel.json
api/以外にファイルを置くとdeploy出来なかったのは、.vercelignoreで無視していたからではないか?
ちゃんと追加すれば認識されるはず
code:vercel.json
{
"functions": {
"api/**/*.jts": { "runtime": "vercel-deno@0.7.6" } },
"routes": [
{
"src": "/png/(.+)",
"dest": "api/plantuml?type=png&url=$1"
},
{
"src": "/svg/(.+)",
"dest": "api/plantuml?type=svg&url=$1"
}
]
}
code:api/plantuml.ts
import { fetchText, fetchImage, respondImage } from "../src/fetch.ts";
export default async (req: ServerRequest) => {
const base = `${req.headers.get("x-forwarded-proto")}://${req.headers.get(
"x-forwarded-host"
)}`;
const url = new URL(req.url, base);
// plantUMLのURLを取得する
const params = url.searchParams;
const plantumlURL = params.get("url");
if (!plantumlURL) {
req.respond({ status: 400, body: "No plantuml URL found." });
return;
}
const imageType = params.get("type");
if (!imageType) {
req.respond({ status: 400, body: "No image type is specified." });
return;
}
req.respond({ status: 400, body: "Image type must be 'png' or 'svg'." });
return;
}
try {
const plantumlBody = await fetchPlantUMLText(svgURL);
const imageData = await fetchPlantUMLImage(plantumlBody, imageType);
respondImage(imageData, req);
} catch (e) {
req.respond({ status: 400, body: e.message });
}
};
code:fetch.ts
import { encode64 } from "./encoder.ts";
import { textToBuffer } from "./converter.ts";
export async function fetchText(url: string) {
const response = await fetch(url);
if (!response.ok) {
throw new Error("Text response is not OK");
}
const text = await response.text();
if (!text.trim()) {
throw new Error(Text is empty);
}
return text;
}
export async function fetchImage(plantUMLText: string, imageType: 'png' | 'svg') {
const response = await fetch(http://www.plantuml.com/plantuml/${imageType}/${encode64(deflate(textToBuffer(text), 9))});
if (!response.ok) {
throw new Error("Failed to fetch a plantUML image from the PlantUML server");
}
const imageData = await response.blob();
}
function respondImage(imageData: Blob, req: ServerRequest) {
const headers = new Headers();
headers.set("Content-Type", ${imageData.type}; charaset=utf-8);
headers.set("Cache-Control", private, max-age=${60 * 60 * 24});
req.respond({
headers,
body: imageData,
});
}
code:converter.ts
export function textToBuffer(text: string) {
const ascii_string = unescape(encodeURIComponent(text)); // 間にこれを噛まさないと文字化けする
let buffer = new Uint8Array(ascii_string.length);
for (let i = 0; i < ascii_string.length; i++) {
bufferi = ascii_string.charCodeAt(i); }
return buffer;
}
code:sample.pu
Bob->Alice : hello
Alice->Bob : 日本語でおk
code:encoder.ts
export function encode64(data: Uint8Array) {
let r = '';
for (let i = 0; i < data.length; i += 3) {
if (i + 2 === data.length) {
r += append3bytes(datai, datai + 1, 0); } else if (i + 1 === data.length) {
r += append3bytes(datai, 0, 0); } else {
}
}
return r;
}
function encode6bit(b: number) {
if (b < 10) return String.fromCharCode(48 + b);
b -= 10;
if (b < 26) return String.fromCharCode(65 + b);
b -= 26;
if (b < 26) return String.fromCharCode(97 + b);
b -= 26;
if (b === 0) return '-';
if (b === 1) return '_';
return '?';
}
function append3bytes(b1: number, b2: number, b3: number) {
const c1 = b1 >> 2;
const c2 = ((b1 & 0x3) << 4) | (b2 >> 4);
const c3 = ((b2 & 0xF) << 2) | (b3 >> 6);
const c4 = b3 & 0x3F;
return encode6bit(c1 & 0x3F)
+ encode6bit(c2 & 0x3F)
+ encode6bit(c3 & 0x3F)
+ encode6bit(c4 & 0x3F);
}